{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "adaf4f6e",
   "metadata": {},
   "source": [
    "# 🔍 NPM Dependency Auditor\n",
    "\n",
    "**Analyze npm packages for security risks and get AI-powered explanations in plain English.**\n",
    "\n",
    "## Features\n",
    "- 📦 Fetch real-time package metadata from NPM registry\n",
    "- 🔒 Basic security risk assessment based on maintenance status\n",
    "- 🤖 AI-powered analysis using Groq (free!)\n",
    "- 📊 Clear, actionable recommendations\n",
    "\n",
    "## Setup Required\n",
    "Add to your `.env` file:\n",
    "```\n",
    "GROQ_API_KEY=your_key_here\n",
    "```\n",
    "Get your free key at: https://console.groq.com/keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d77fbd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import json\n",
    "from datetime import datetime\n",
    "from openai import OpenAI\n",
    "import os\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# Load environment variables\n",
    "load_dotenv(override=True)\n",
    "\n",
    "print(\"✅ Setup complete!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99948480",
   "metadata": {},
   "source": [
    "## 1️⃣ Fetch Package Information from NPM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "354f23c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fetch_npm_package_info(package_name):\n",
    "    \"\"\"\n",
    "    Fetch package information from NPM registry\n",
    "    \n",
    "    Args:\n",
    "        package_name: Name of package (e.g., 'express' or 'axios@0.21.1')\n",
    "    \n",
    "    Returns:\n",
    "        Dictionary with package metadata\n",
    "    \"\"\"\n",
    "    # Parse package name and version\n",
    "    if '@' in package_name and not package_name.startswith('@'):\n",
    "        name, version = package_name.split('@')\n",
    "    else:\n",
    "        name = package_name\n",
    "        version = 'latest'\n",
    "    \n",
    "    try:\n",
    "        # Fetch from NPM registry API\n",
    "        url = f\"https://registry.npmjs.org/{name}\"\n",
    "        response = requests.get(url, timeout=10)\n",
    "        response.raise_for_status()\n",
    "        \n",
    "        data = response.json()\n",
    "        \n",
    "        # Get latest version info\n",
    "        latest_version = data['dist-tags']['latest']\n",
    "        version_data = data['versions'][latest_version]\n",
    "        \n",
    "        # Calculate days since last update\n",
    "        last_modified = data['time'][latest_version]\n",
    "        last_modified_date = datetime.fromisoformat(last_modified.replace('Z', '+00:00'))\n",
    "        days_ago = (datetime.now(last_modified_date.tzinfo) - last_modified_date).days\n",
    "        \n",
    "        # Extract useful info\n",
    "        package_info = {\n",
    "            'name': name,\n",
    "            'version': latest_version,\n",
    "            'description': data.get('description', 'No description'),\n",
    "            'last_updated': last_modified,\n",
    "            'days_since_update': days_ago,\n",
    "            'homepage': data.get('homepage', 'N/A'),\n",
    "            'repository': data.get('repository', {}).get('url', 'N/A'),\n",
    "            'license': version_data.get('license', 'N/A'),\n",
    "            'maintainers': len(data.get('maintainers', [])),\n",
    "            'keywords': data.get('keywords', [])\n",
    "        }\n",
    "        \n",
    "        return package_info\n",
    "    \n",
    "    except requests.exceptions.RequestException as e:\n",
    "        return {'error': f\"Failed to fetch package: {str(e)}\"}\n",
    "    except KeyError as e:\n",
    "        return {'error': f\"Package not found or invalid data: {str(e)}\"}\n",
    "\n",
    "print(\"✅ fetch_npm_package_info() defined\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94ed2a4f",
   "metadata": {},
   "source": [
    "## 2️⃣ Check for Security Risks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05f13d1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def check_vulnerabilities_basic(package_name):\n",
    "    \"\"\"\n",
    "    Basic vulnerability check using NPM audit data\n",
    "    For MVP, we'll simulate this - later we can use Snyk/OSV API\n",
    "    \"\"\"\n",
    "    # For now, we'll mark packages as potentially risky based on age\n",
    "    # In production, you'd call APIs like:\n",
    "    # - https://api.osv.dev/v1/query\n",
    "    # - https://snyk.io/api\n",
    "    \n",
    "    info = fetch_npm_package_info(package_name)\n",
    "    \n",
    "    if 'error' in info:\n",
    "        return {'status': 'unknown', 'reason': info['error']}\n",
    "    \n",
    "    days = info['days_since_update']\n",
    "    \n",
    "    # Simple heuristic for MVP\n",
    "    if days > 730:  # 2 years\n",
    "        return {\n",
    "            'status': 'high_risk',\n",
    "            'reason': 'Package not updated in over 2 years (abandoned?)',\n",
    "            'days_since_update': days\n",
    "        }\n",
    "    elif days > 365:  # 1 year\n",
    "        return {\n",
    "            'status': 'medium_risk',\n",
    "            'reason': 'Package not updated in over 1 year',\n",
    "            'days_since_update': days\n",
    "        }\n",
    "    else:\n",
    "        return {\n",
    "            'status': 'low_risk',\n",
    "            'reason': 'Recently maintained',\n",
    "            'days_since_update': days\n",
    "        }\n",
    "\n",
    "print(\"✅ check_vulnerabilities_basic() defined\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e501a85",
   "metadata": {},
   "source": [
    "## 3️⃣ AI-Powered Analysis (Using Groq)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "013cec37",
   "metadata": {},
   "outputs": [],
   "source": [
    "def analyze_with_ai(package_info, vulnerability_info):\n",
    "    \"\"\"\n",
    "    Use Groq (free API) instead of GPT-4\n",
    "    \"\"\"\n",
    "    # Groq uses OpenAI-compatible API\n",
    "    groq_client = OpenAI(\n",
    "        api_key=os.getenv(\"GROQ_API_KEY\"),  # Add to .env file\n",
    "        base_url=\"https://api.groq.com/openai/v1\"\n",
    "    )\n",
    "    \n",
    "    prompt = f\"\"\"You are a senior software security analyst. Analyze this npm package and provide a clear, concise risk assessment.\n",
    "\n",
    "Package Information:\n",
    "- Name: {package_info.get('name')}\n",
    "- Version: {package_info.get('version')}\n",
    "- Description: {package_info.get('description')}\n",
    "- Last Updated: {package_info.get('days_since_update')} days ago\n",
    "- Maintainers: {package_info.get('maintainers')}\n",
    "- License: {package_info.get('license')}\n",
    "\n",
    "Preliminary Risk Assessment:\n",
    "- Status: {vulnerability_info.get('status')}\n",
    "- Reason: {vulnerability_info.get('reason')}\n",
    "\n",
    "Provide your analysis in this format:\n",
    "\n",
    "**RISK LEVEL**: [LOW/MEDIUM/HIGH]\n",
    "\n",
    "**PLAIN ENGLISH SUMMARY**:\n",
    "[2-3 sentences explaining what this package does and its overall safety]\n",
    "\n",
    "**GOOD SIGNS** (if any):\n",
    "- [List positive indicators]\n",
    "\n",
    "**CONCERNS** (if any):\n",
    "- [List potential issues]\n",
    "\n",
    "**RECOMMENDATION**:\n",
    "[Clear action: \"Safe to use\" / \"Update immediately\" / \"Consider alternatives\" etc.]\n",
    "\n",
    "Keep it concise, non-technical, and actionable.\"\"\"\n",
    "\n",
    "    try:\n",
    "        response = groq_client.chat.completions.create(\n",
    "            model=\"llama-3.3-70b-versatile\",  # Updated model (Dec 2024)\n",
    "            messages=[\n",
    "                {\"role\": \"system\", \"content\": \"You are a helpful security analyst who explains technical concepts in simple terms.\"},\n",
    "                {\"role\": \"user\", \"content\": prompt}\n",
    "            ],\n",
    "            temperature=0.7,\n",
    "            max_tokens=800\n",
    "        )\n",
    "        \n",
    "        return response.choices[0].message.content\n",
    "    \n",
    "    except Exception as e:\n",
    "        return f\"Error with Groq: {str(e)}\"\n",
    "\n",
    "print(\"✅ analyze_with_ai() defined - using Groq API\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "980b1a50",
   "metadata": {},
   "source": [
    "## 4️⃣ Main Analysis Pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18040738",
   "metadata": {},
   "outputs": [],
   "source": [
    "def analyze_package(package_name):\n",
    "    \"\"\"\n",
    "    Complete package analysis pipeline\n",
    "    \"\"\"\n",
    "    print(f\"🔍 Analyzing package: {package_name}\")\n",
    "    print(\"=\" * 70)\n",
    "    \n",
    "    # Step 1: Fetch package info\n",
    "    print(\"\\n📦 Fetching package data from NPM...\")\n",
    "    package_info = fetch_npm_package_info(package_name)\n",
    "    \n",
    "    if 'error' in package_info:\n",
    "        print(f\"❌ Error: {package_info['error']}\")\n",
    "        return\n",
    "    \n",
    "    print(f\"✅ Found: {package_info['name']}@{package_info['version']}\")\n",
    "    \n",
    "    # Step 2: Check for vulnerabilities\n",
    "    print(\"\\n🔒 Checking security status...\")\n",
    "    vuln_info = check_vulnerabilities_basic(package_name)\n",
    "    print(f\"✅ Status: {vuln_info['status']}\")\n",
    "    \n",
    "    # Step 3: Analyze with AI\n",
    "    print(\"\\n🤖 Analyzing with AI...\")\n",
    "    ai_analysis = analyze_with_ai(package_info, vuln_info)\n",
    "    \n",
    "    # Display results\n",
    "    print(\"\\n\" + \"=\" * 70)\n",
    "    print(\"📊 PACKAGE DETAILS\")\n",
    "    print(\"=\" * 70)\n",
    "    print(f\"Name:         {package_info['name']}\")\n",
    "    print(f\"Version:      {package_info['version']}\")\n",
    "    print(f\"Description:  {package_info['description']}\")\n",
    "    print(f\"Last Updated: {package_info['days_since_update']} days ago\")\n",
    "    print(f\"Maintainers:  {package_info['maintainers']}\")\n",
    "    print(f\"License:      {package_info['license']}\")\n",
    "    print(f\"Repository:   {package_info['repository']}\")\n",
    "    \n",
    "    print(\"\\n\" + \"=\" * 70)\n",
    "    print(\"🤖 AI SECURITY ANALYSIS\")\n",
    "    print(\"=\" * 70)\n",
    "    print(ai_analysis)\n",
    "    print(\"\\n\" + \"=\" * 70)\n",
    "    print(f\"Report generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\")\n",
    "    print(\"⚠️  Use at your own risk. This is for informational purposes only.\")\n",
    "    print(\"=\" * 70)\n",
    "\n",
    "print(\"✅ Main analysis function ready!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afe2b5b6",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 🧪 Examples & Testing\n",
    "\n",
    "### Example 1: Well-Maintained Package (Express)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb4f96b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Test with a safe, well-maintained package\n",
    "analyze_package('express')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f19dd52a",
   "metadata": {},
   "source": [
    "### Example 2: Abandoned Package (Left-Pad)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2223626a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Test with an abandoned package\n",
    "analyze_package('left-pad')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
